/*
 * @Descripttion : QianFox让数字化营销更简单
 * @Author       : QianFox Team
 * @Date         : 2024-02-20 18:01:14
 * @Version      : V1.08
 * @Copyright    : ©2021-现在 贵州黔狐科技股份有限公司 版权所有
 * @LastEditors  : QianFox Team
 * @LastEditTime : 2024-02-26 20:28:34
 */
import { observer } from './common/observer';

function Scrollspy() {
    // class 类
    this.NAV = '.foxui-scrollspy-nav';
    this.ACTION = '.foxui-scrollspy-action';
    this.CONTENT = '.foxui-scrollspy-content';
    this.ITEM = '.foxui-scrollspy-item';
}

Scrollspy.prototype._init = function () {
    this._bindScroll();
    this._bindClick();
};

Scrollspy.prototype.init = function () {
    this._bindScroll();
};

/**
 * @description: 绑定监听滚动事件
 * @return {*}
 * @Date: 2024-02-22 10:30:11
 */
Scrollspy.prototype._bindScroll = function () {
    let that = this;
    // 绑定监听指定父级元素滚动事件
    $(`${this.CONTENT}[data-spy="scroll"]`).each(function (index, item) {
        that._listenerScroll(item);
    });
    // 绑定监听 Window 滚动事件
    let $content = $(`${this.CONTENT}[data-spy!="scroll"]`);
    if ($content.length > 0) this._listenerScroll($content[0], true);
};

/**
 * @description: 指定父级元素添加监听事件
 * @param {*} parentElement      容器 DOM 节点
 * @param {*} isWindow           滚动容器是不是 window，如不是，则为 parentElement
 * @return {*}
 * @Date: 2024-02-22 10:51:59
 */
Scrollspy.prototype._listenerScroll = function (parentElement, isWindow) {
    let ticking = false;

    // 当滚动时，需执行的函数
    const handleScroll = () => {
        let offset = parseInt($(parentElement).attr('data-offset')) ? parseInt($(parentElement).attr('data-offset')) : 0,
            selectElements = parentElement.querySelectorAll(this.ITEM),
            currentItem = this._calculateCurrentItem(isWindow ? window : parentElement, selectElements, offset);

        // 设置当前激活的锚点
        let curId = $(currentItem).attr('id'),
            $curAction = $(`.foxui-scrollspy-action[href$="#${curId}"]`);
        // 移除 is-active
        $curAction
            .closest(this.NAV)
            .find(this.ACTION + '.is-active')
            .removeClass('is-active');
        // 添加 is-active
        $curAction.addClass('is-active');
    };

    // 防止拖动过快，导航计算出错
    const _scrollspyScrollHandler = () => {
        if (!ticking) {
            window.requestAnimationFrame(function () {
                handleScroll();
                ticking = false;
            });
            ticking = true;
        }
    };

    if (isWindow) {
        // 移出监听事件，避免重复多次监听
        window.removeEventListener('scroll', _scrollspyScrollHandler);
        // 添加监听事件
        window.addEventListener('scroll', _scrollspyScrollHandler);
    } else {
        // 移出监听事件，避免重复多次监听
        parentElement.removeEventListener('scroll', _scrollspyScrollHandler);
        // 添加监听事件
        parentElement.addEventListener('scroll', _scrollspyScrollHandler);
    }
};

/**
 * @description: 绑定点击锚点事件
 * @return {*}
 * @Date: 2024-02-22 10:29:36
 */
Scrollspy.prototype._bindClick = function () {
    let that = this;
    $(document).on('click', this.ACTION, function (event) {
        event.preventDefault();
        // 获取目标锚点的ID
        const targetId = $(this).attr('href').split('#')[1];
        // 获取目标锚点的元素
        const $target = $(`#${targetId}`),
            $content = $target.closest(that.CONTENT),
            targetElement = $target[0],
            contentElement = $content.attr('data-spy') === 'scroll' ? $content[0] : window,
            behavior = $content.attr('data-smooth-scroll') === 'false' ? 'auto' : 'smooth',
            offset = parseInt($content.attr('data-offset')) ? parseInt($content.attr('data-offset')) : 0;
        // 滚动到目标锚点位置
        if (targetElement) {
            contentElement.scrollTo({
                top: targetElement.offsetTop - offset,
                behavior,
            });
        }
    });
};

/**
 * @description: 计算出当前元素
 * @param {*} parentNode         父元素DOM元素
 * @param {*} spyItems           需要判断的元素所有节点
 * @param {*} offset             偏移值
 * @param {*} error              误差值
 * @return {*}
 * @Date: 2024-02-24 18:15:58
 */
Scrollspy.prototype._calculateCurrentItem = function (parentNode, spyItems, offset = 0, error = 60) {
    let scrollContainerHeight,
        scrollTop,
        directionUp = false;

    if (parentNode === window) {
        scrollContainerHeight = $(window).height();
        scrollTop = $(window).scrollTop();
    } else {
        scrollContainerHeight = $(parentNode).outerHeight();
        scrollTop = $(parentNode).scrollTop();
    }

    let currentItem,
        minDistance = Number.MAX_VALUE,
        lastScrollTop = 0;

    directionUp = scrollTop < lastScrollTop;

    lastScrollTop = scrollTop;

    $(spyItems).each(function () {
        let offsetTop;

        if (parentNode === window) {
            offsetTop = $(this).offset().top - offset;
        } else {
            offsetTop = $(this).offset().top - $(parentNode).offset().top + scrollTop - offset;
        }
        // 添加元素的高度以计算到元素底部的距离
        const offsetBottom = offsetTop + $(this).outerHeight();
        let distance;

        if ($(this).outerHeight() > scrollContainerHeight) {
            if (directionUp) {
                distance = Math.abs(scrollTop - offsetBottom);
            } else {
                distance = Math.abs(scrollTop - offsetTop);
            }
        } else {
            distance = Math.abs(scrollTop - offsetTop);
        }

        // 仅当新计算的距离小于偏移量时更新当前项。
        if (distance + error < minDistance) {
            minDistance = distance;
            currentItem = this;
        }
    });

    return currentItem;
};

let scrollspy = new Scrollspy();

export { scrollspy };
